P2680 运输计划

闲扯

我发现自己的思路出现了严重的偏差。。。

想了两个假算法,都是代码打完发现有问题。。

题面

题面

Solution

本题要解决的问题:将一条边的权值变为 $0$ ,使得所有路径长度的最大值最小。

我们考虑当 $x$ 满足条件时,所有比 $x$ 的也一定满足,即答案具有单调性,所以我们可以用二分答案。

现在我们将求解问题变为了判定性问题,理论上来说实现难度是要小一些的。

考虑怎么判断可行。

对于当前的判定值 $x$ ,所有长度大于 $x$ 的路径必须被处理掉。

因为只能清空一条边,所以我们选择的边一定属于所有这样的路径。

根据贪心的思想,我们取所有的满足条件的边中长度最大的一条。

如果最长路径的长度减去我们所选边的长度依旧大于 $x$ ,说明 $x$ 不合法,否则一定可以确定合法。

现在的问题就只剩下怎么判定一条边是否属于所有的长度大于 $x$ 的路径。

朴素的想法是对于每一个长度大于 $x$ 的路径,我们将路径上的所有边标记一次。

最后被标记了 $m$ 次的( $m$ 为这种路径的个数)就是我们要找的边。

考虑优化。

对于树上路径的区间加,可以考虑差分。

$u,v$ 两点间的所有边的计数加 $1$ ,可以转化为 $c_u+1,c_v+1,c_{lca}-2$ 。其中 $c$ 为差分数组。

这里我们需要要到一个小技巧:将每一条边寄存在深度更深的那一个点上。可以证明除了根节点外,每个点刚好代表了一条边。

这样我们就将所有的问题都解决了,可以轻松 $A$ 题了

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include<bits/stdc++.h>
#define del(a,i) memset(a,i,sizeof(a))
#define ll long long
#define inl inline
#define il inl void
#define it inl int
#define ill inl ll
#define re register
#define ri re int
#define rl re ll
#define mid ((l+r)>>1)
#define lowbit(x) (x&(-x))
#define INF 0x3f3f3f3f
using namespace std;
template<class T>il read(T &x){
int f=1;char k=getchar();x=0;
for(;k>'9'||k<'0';k=getchar()) if(k=='-') f=-1;
for(;k>='0'&&k<='9';k=getchar()) x=(x<<3)+(x<<1)+k-'0';
x*=f;
}
template<class T>il print(T x){
if(x/10) print(x/10);
putchar(x%10+'0');
}
ll mul(ll a,ll b,ll mod){long double c=1.;return (a*b-(ll)(c*a*b/mod)*mod)%mod;}
it qpow(int x,int m,int mod){
int res=1,bas=x%mod;
while(m){
if(m&1) res=(1ll*res*bas)%mod;
bas=(1ll*bas*bas)%mod,m>>=1;
}
return res%mod;
}
it max(int x,int y){return x>y?x:y;}
const int MAXN = 3e5+5;
int n,m,u,v,t,head[MAXN],num_edge,dis[MAXN],l,r,c[MAXN],sum[MAXN],mx,num;
struct Edge{
int next,to,dis;
Edge(){}
Edge(int next,int to,int dis):next(next),to(to),dis(dis){}
}edge[MAXN<<1];
il add_edge(int u,int v,int dis){
edge[++num_edge]=Edge(head[u],v,dis),head[u]=num_edge;
edge[++num_edge]=Edge(head[v],u,dis),head[v]=num_edge;
}
struct Node{
int u,v,lca,len;
}node[MAXN];
int d[MAXN],f[MAXN],sz[MAXN],top[MAXN],son[MAXN],lca;
il DFS1(int u,int fa){
f[u]=fa,d[u]=d[fa]+1,sz[u]=1;
for(ri i=head[u];i;i=edge[i].next){
if(edge[i].to==fa) continue;
dis[edge[i].to]=dis[u]+edge[i].dis;
DFS1(edge[i].to,u),sz[u]+=sz[edge[i].to];
if(sz[edge[i].to]>sz[son[u]]) son[u]=edge[i].to;
}
}
il DFS2(int u,int t){
top[u]=t;
if(!son[u]) return ;
DFS2(son[u],t);
for(ri i=head[u];i;i=edge[i].next){
if(edge[i].to==f[u]||edge[i].to==son[u]) continue;
DFS2(edge[i].to,edge[i].to);
}
}
it LCA(int u,int v){
while(top[u]!=top[v]){
if(d[top[u]]<d[top[v]]) swap(u,v);
u=f[top[u]];
}
return d[u]<d[v]?u:v;
}
il DFS(int u){
sum[u]=c[u];
for(ri i=head[u];i;i=edge[i].next){
if(edge[i].to==f[u]) continue;
DFS(edge[i].to),sum[u]+=sum[edge[i].to];
if(sum[edge[i].to]>=num) mx=max(mx,edge[i].dis);
}
}
inl bool check(int lim){
ri mx_len=0;mx=0,num=0,del(c,0);
for(ri i=1;i<=m;++i){
if(node[i].len<=lim) continue;
c[node[i].u]++,c[node[i].v]++,c[node[i].lca]-=2;
num++,mx_len=max(mx_len,node[i].len);
}
DFS(1);
if(mx_len-mx<=lim) return true;
return false;
}
int main()
{
// freopen("testdata.in","r",stdin);
// freopen(".out","w",stdout);
read(n),read(m);
for(ri i=1;i<n;++i) read(u),read(v),read(t),add_edge(u,v,t);
DFS1(1,0),DFS2(1,1);
for(ri i=1;i<=m;++i){
read(u),read(v),lca=LCA(u,v);
node[i].len=dis[u]+dis[v]-2*dis[lca];
r=max(r,node[i].len),node[i].u=u,node[i].v=v,node[i].lca=lca;
}
while(l<r){
if(check(mid)) r=mid;
else l=mid+1;
}
print(l);
return 0;
}

总结

对于最大值最小,最小值最大这类问题要敏感一些,可以往二分这方面想。